"""
GT-LMS系统的认证系统实现
此模块提供用户认证和会话管理功能
"""

import hashlib
import time
from typing import Dict, Optional, Set
from datetime import datetime, timedelta

from utils.helpers import hash_password, verify_password
from utils.exceptions import AuthenticationException, PermissionException
from models.user import User
from managers.user_manager import UserManager


class AuthenticationSystem:
    """认证系统类 - Bonus功能"""

    def __init__(self, max_attempts: int = 5, lockout_duration: int = 300):
        """
        初始化认证系统

        参数:
            max_attempts (int): 最大登录尝试次数
            lockout_duration (int): 锁定持续时间（秒）
        """
        self.current_user: Optional[User] = None
        self.session_token: Optional[str] = None
        self.session_start_time: Optional[datetime] = None
        self.session_timeout: int = 3600  # 1小时会话超时

        # 登录尝试记录
        self.login_attempts: Dict[str, int] = {}
        self.locked_accounts: Dict[str, datetime] = {}
        self.max_attempts: int = max_attempts
        self.lockout_duration: int = lockout_duration

        # 会话令牌存储
        self.active_sessions: Dict[str, Dict] = {}
        
        # 用户管理器实例
        self.user_manager = None
    
    def set_user_manager(self, user_manager):
        """
        设置用户管理器实例
        
        参数:
            user_manager: 用户管理器实例
        """
        self.user_manager = user_manager

    def login(self, email: str, password: str) -> bool:
        """
        用户登录

        参数:
            email (str): 用户邮箱
            password (str): 用户密码

        返回:
            bool: 登录成功返回True，否则返回False

        异常:
            AuthenticationException: 认证失败时抛出
        """
        try:
            # 检查账户是否被锁定
            if self.is_account_locked(email):
                raise AuthenticationException("Account is locked; please try again later")

            # 验证凭据
            if not self.validate_credentials(email, password):
                # 记录失败的登录尝试
                self.record_failed_login(email)
                raise AuthenticationException("Email or password is incorrect")

            # 重置登录尝试计数
            self.reset_login_attempts(email)

            # 登录成功，设置当前用户和会话
            # 注意：validate_credentials已经设置了current_user
            self.session_token = self.generate_token()
            self.session_start_time = datetime.now()

            # 存储会话信息
            self.active_sessions[self.session_token] = {
                'email': email,
                'start_time': self.session_start_time,
                'last_activity': self.session_start_time
            }

            return True

        except AuthenticationException:
            raise
        except Exception as e:
            raise AuthenticationException(f"An error occurred during login: {str(e)}")

    def logout(self) -> None:
        """
        用户登出
        """
        if self.session_token:
            # 从活动会话中移除
            if self.session_token in self.active_sessions:
                del self.active_sessions[self.session_token]

            self.session_token = None
            self.session_start_time = None
            self.current_user = None

    def validate_credentials(self, email: str, password: str) -> bool:
        """
        验证用户凭据

        参数:
            email (str): 用户邮箱
            password (str): 用户密码

        返回:
            bool: 凭据有效返回True，否则返回False
        """
        try:
            # 确保用户管理器已设置
            if not self.user_manager:
                print("Error: user manager is not set")
                return False
            
            # 根据邮箱查找用户
            user = self.user_manager.get_user_by_email(email)
            if not user:
                return False
            
            # 验证密码
            if not user.verify_password(password):
                return False
            
            # 设置当前用户
            self.current_user = user
            return True
            
        except Exception as e:
            print(f"An error occurred while validating credentials: {str(e)}")
            return False

    def reset_password(self, email: str) -> str:
        """
        重置用户密码

        参数:
            email (str): 用户邮箱

        返回:
            str: 新生成的临时密码

        异常:
            AuthenticationException: 重置密码失败时抛出
        """
        try:
            # 检查用户是否存在
            # user = user_manager.get_user_by_email(email)
            # if not user:
            #     raise AuthenticationException("用户不存在")

            # 生成临时密码
            import random
            import string
            temp_password = ''.join(random.choices(string.ascii_letters + string.digits, k=12))

            # 哈希新密码并更新用户记录
            # hashed_password = hash_password(temp_password)
            # user.update_password(hashed_password)

            # 发送邮件通知用户（实际实现中）
            # send_password_reset_email(email, temp_password)

            return temp_password

        except Exception as e:
            raise AuthenticationException(f"Password reset failed: {str(e)}")

    def check_permission(self, user: User, action: str) -> bool:
        """
        检查用户权限

        参数:
            user (User): 用户对象
            action (str): 请求的操作

        返回:
            bool: 有权限返回True，否则返回False
        """
        # 基于用户角色的权限检查
        user_permissions = user.get_permissions()

        # 检查用户是否具有特定权限
        return action in user_permissions

    def generate_token(self) -> str:
        """
        生成会话令牌

        返回:
            str: 生成的会话令牌
        """
        # 生成基于时间戳和随机数的令牌
        import uuid
        token = hashlib.sha256(f"{uuid.uuid4()}{time.time()}".encode()).hexdigest()
        return token

    def validate_token(self, token: str) -> bool:
        """
        验证会话令牌

        参数:
            token (str): 要验证的令牌

        返回:
            bool: 令牌有效返回True，否则返回False
        """
        if not token:
            return False

        # 检查令牌是否存在
        if token not in self.active_sessions:
            return False

        # 检查会话是否过期
        session_info = self.active_sessions[token]
        last_activity = session_info['last_activity']

        if datetime.now() - last_activity > timedelta(seconds=self.session_timeout):
            # 会话已过期，删除它
            del self.active_sessions[token]
            return False

        # 更新最后活动时间
        self.active_sessions[token]['last_activity'] = datetime.now()
        return True

    def is_account_locked(self, email: str) -> bool:
        """
        检查账户是否被锁定

        参数:
            email (str): 用户邮箱

        返回:
            bool: 账户被锁定返回True，否则返回False
        """
        if email not in self.locked_accounts:
            return False

        # 检查锁定是否已过期
        lock_time = self.locked_accounts[email]
        if datetime.now() - lock_time > timedelta(seconds=self.lockout_duration):
            # 锁定已过期，解锁账户
            del self.locked_accounts[email]
            return False

        return True

    def record_failed_login(self, email: str) -> None:
        """
        记录失败的登录尝试

        参数:
            email (str): 用户邮箱
        """
        if email not in self.login_attempts:
            self.login_attempts[email] = 0

        self.login_attempts[email] += 1

        # 检查是否达到最大尝试次数
        if self.login_attempts[email] >= self.max_attempts:
            self.lock_account(email)

    def reset_login_attempts(self, email: str) -> None:
        """
        重置登录尝试计数

        参数:
            email (str): 用户邮箱
        """
        if email in self.login_attempts:
            del self.login_attempts[email]

    def lock_account(self, email: str) -> None:
        """
        锁定账户

        参数:
            email (str): 用户邮箱
        """
        self.locked_accounts[email] = datetime.now()

    def unlock_account(self, email: str) -> None:
        """
        解锁账户

        参数:
            email (str): 用户邮箱
        """
        if email in self.login_attempts:
            del self.login_attempts[email]
        if email in self.locked_accounts:
            del self.locked_accounts[email]

    def get_session_info(self) -> Dict:
        """
        获取当前会话信息

        返回:
            Dict: 会话信息
        """
        if not self.session_token or not self.validate_token(self.session_token):
            return {}

        return self.active_sessions.get(self.session_token, {})

    def cleanup_expired_sessions(self) -> None:
        """
        清理过期会话
        """
        current_time = datetime.now()
        expired_tokens = []

        for token, session_info in self.active_sessions.items():
            last_activity = session_info['last_activity']
            if current_time - last_activity > timedelta(seconds=self.session_timeout):
                expired_tokens.append(token)

        for token in expired_tokens:
            del self.active_sessions[token]

    def get_current_user(self) -> Optional[User]:
        """
        获取当前登录用户

        返回:
            Optional[User]: 当前登录的用户对象，如果未登录则返回None
        """
        if self.session_token and self.validate_token(self.session_token):
            return self.current_user
        return None

    def is_logged_in(self) -> bool:
        """
        检查用户是否已登录

        返回:
            bool: 已登录返回True，否则返回False
        """
        return self.get_current_user() is not None

    def get_user_session(self, email: str) -> Optional[Dict]:
        """
        获取指定用户的会话信息

        参数:
            email (str): 用户邮箱

        返回:
            Optional[Dict]: 用户会话信息，如果用户未登录则返回None
        """
        # 查找与邮箱关联的会话令牌
        for token, session_info in self.active_sessions.items():
            if session_info.get('email') == email:
                return session_info
        return None